package org.darcy.youchat.library.videorecordview;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 视频录制控件
 */
public class YouChatVideoRecordView extends SurfaceView implements MediaRecorder.OnErrorListener {

    private Context mContext;

    private SurfaceHolder mSurfaceHolder;
    private MediaRecorder mMediaRecorder;

    //Camera2用管道的概念将安卓设备和摄像头之间连通，应用向设备发送请求，设备返回结果，再由应用处理结果。
    private CameraManager cameraManager;
    private HandlerThread handlerThread;
    private Handler handler;
    private Size previewSize;
    private CameraDevice mCamera;
    private ImageReader imageReader;
    private CameraCaptureSession cameraCaptureSession;
    private CaptureRequest.Builder previewBuilder;
    private CaptureRequest.Builder captureBuilder;
    //前置摄像头
    private int currentCameraId = CameraCharacteristics.LENS_FACING_FRONT;


    private Timer mTimer;
    private OnRecordStatusChangeListener mOnRecordStatusChangeListener;// 录制状态变化回调接口

    private int mXpx;//视频分辨率宽度
    private int mYpx;//视频分辨率高度
    private int mOutFormat;//0是mp4，1是3gp
    private int mRecordMaxTime;// 一次拍摄最长时间
    private int mVideoEncodingBitRate;//声音的编码位率
    private int mVideoFrameRate;//录制的视频帧率

    private String mOutputDirPath = Environment.getExternalStorageDirectory()
            + File.separator + "YouChat/video/";//输出目录
    private String mSuffix;//视频文件后缀

    private boolean mIsOpenCamera;// 是否一开始就打开摄像头
    private int mTimeCount;// 时间计数
    private File mVideoFile = null;// 视频输出文件


    //打开相机回调
    private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            //相机开启，打开预览
            mCamera = camera;
            startPreview();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            //相机关闭
            camera.close();
            mCamera = null;
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            //相机报错
            camera.close();
            mCamera = null;
        }
    };

    //创建session回调
    private CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
            try {
                cameraCaptureSession = session;
                //自动对焦
                previewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                cameraCaptureSession.setRepeatingRequest(previewBuilder.build(), null, handler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
            session.close();
            cameraCaptureSession = null;
            mCamera.close();
            mCamera = null;
        }
    };

    //拍完照回调
    private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session,
                                        @NonNull CaptureRequest request,
                                        @NonNull CaptureResult partialResult) {
            super.onCaptureProgressed(session, request, partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                       @NonNull CaptureRequest request,
                                       @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
            try {
                //自动对焦
                captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
                //重新打开预览
                session.setRepeatingRequest(previewBuilder.build(), null, handler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onCaptureFailed(@NonNull CameraCaptureSession session,
                                    @NonNull CaptureRequest request,
                                    @NonNull CaptureFailure failure) {
            super.onCaptureFailed(session, request, failure);
            cameraCaptureSession.close();
            cameraCaptureSession = null;
            mCamera.close();
            mCamera = null;
        }
    };

    /**
     * 打开相机
     */
    public void openCamera() {
        try {
            //获取属性CameraDevice属性描述
            CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(String.valueOf(currentCameraId));
            //获取摄像头支持的配置属性
            StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            previewSize = getMaxSize(map.getOutputSizes(SurfaceHolder.class));
            //第一个参数指定哪个摄像头，第二个参数打开摄像头的状态回调，第三个参数是运行在哪个线程(null是当前线程)
            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            cameraManager.openCamera(String.valueOf(currentCameraId), stateCallback, handler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

    }

    /**
     * 开始预览
     */
    private void startPreview() {
        try {
            previewBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            //设置预览数据输出界面
            previewBuilder.addTarget(mSurfaceHolder.getSurface());
            //创建相机捕获会话，
            //第一个参数是捕获数据的输出Surface列表，第二个参数是CameraCaptureSession状态回调接口，第三个参数是在哪个线程(null是当前线程)
            mCamera.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(), imageReader.getSurface()), sessionStateCallback, handler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    /**
     * 拍照
     */
    private void takePhoto() {
        try {
            captureBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(imageReader.getSurface());
            //自动对焦
            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            cameraCaptureSession.stopRepeating();
            //拍照
            cameraCaptureSession.capture(captureBuilder.build(), captureCallback, handler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 切换摄像头
     */
    private void switchCamera() {
        try {
            for (String cameraId : cameraManager.getCameraIdList()) {
                CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                Size maxSize = getMaxSize(map.getOutputSizes(SurfaceHolder.class));
                if (currentCameraId == CameraCharacteristics.LENS_FACING_BACK && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {
                    //前置转后置
                    previewSize = maxSize;
                    currentCameraId = CameraCharacteristics.LENS_FACING_FRONT;
                    mCamera.close();
                    openCamera();
                    break;
                } else if (currentCameraId == CameraCharacteristics.LENS_FACING_FRONT && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) {
                    //后置转前置
                    previewSize = maxSize;
                    currentCameraId = CameraCharacteristics.LENS_FACING_BACK;
                    mCamera.close();
                    openCamera();
                    break;
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭相机
     */
    private void closeCamera() {
        //关闭捕捉会话
        if (cameraCaptureSession != null) {
            cameraCaptureSession.close();
            cameraCaptureSession = null;
        }
        //关闭相机
        if (mCamera != null) {
            mCamera.close();
            mCamera = null;
        }
        //关闭拍照处理器
        if (imageReader != null) {
            imageReader.close();
            imageReader = null;
        }
    }

    /**
     * 获取最大预览尺寸
     */
    private Size getMaxSize(Size[] outputSizes) {
        Size sizeMax = null;
        if (outputSizes != null) {
            sizeMax = outputSizes[0];
            for (Size size : outputSizes) {
                if (size.getWidth() * size.getHeight() > sizeMax.getWidth() * sizeMax.getHeight()) {
                    sizeMax = size;
                }
            }
        }
        return sizeMax;
    }


    public YouChatVideoRecordView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public YouChatVideoRecordView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VideoRecordView, defStyleAttr, 0);
        mXpx = typedArray.getInteger(R.styleable.VideoRecordView_vrv_x_px, 320);// 默认320
        mYpx = typedArray.getInteger(R.styleable.VideoRecordView_vrv_y_px, 240);// 默认240
        mOutFormat = typedArray.getInteger(R.styleable.VideoRecordView_vrv_out_format, 1);//默认3gp
        mRecordMaxTime = typedArray.getInteger(R.styleable.VideoRecordView_vrv_record_max_time, 10);//默认最长10秒
        mVideoEncodingBitRate = typedArray.getInteger(R.styleable.VideoRecordView_vrv_video_encoding_bit_rate, 1024 * 1024);
        mVideoFrameRate = typedArray.getInteger(R.styleable.VideoRecordView_vrv_video_frame_rate, 10);
        mIsOpenCamera = typedArray.getBoolean(R.styleable.VideoRecordView_vrv_is_open_camera, true);
        typedArray.recycle();

        switch (mOutFormat) {
            case 0:
                mSuffix = ".mp4";
                break;
            case 1:
                mSuffix = ".3gp";
                break;
        }

        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(new CustomCallBack());
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }


    private class CustomCallBack implements SurfaceHolder.Callback {

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            if (!mIsOpenCamera)
                return;
            openCamera();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            if (!mIsOpenCamera)
                return;
            freeCameraResource();
        }
    }

    /**
     * 初始化摄像头
     */
    public void initCamera(Context context) {
        mContext = context;
        cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        //Camera2全程异步
        handlerThread = new HandlerThread("Camera2");
        handlerThread.start();
        handler = new Handler(handlerThread.getLooper());
        mSurfaceHolder = this.getHolder();
        mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                openCamera();
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                closeCamera();
            }
        });
    }


    /**
     * 释放摄像头资源
     */
    private void freeCameraResource() {
        if (mCamera != null) {
            mCamera = null;
        }
    }

    private void createRecordDir() {
        //录制的视频保存文件夹
        File sampleDir = new File(mOutputDirPath);
        if (!sampleDir.exists()) {
            sampleDir.mkdirs();
        }
        //创建文件
        try {
            mVideoFile = File.createTempFile("recording", mSuffix, sampleDir);
        } catch (IOException e) {
            Log.e("YouChatVideoRecordView", "创建视频文件失败");
        }
    }

    private void initRecord() throws IOException {
        mMediaRecorder = new MediaRecorder();
        try {
            mMediaRecorder.reset();
            mMediaRecorder.setOnErrorListener(this);
            mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//视频源
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);//音频源
            mMediaRecorder.setOrientationHint(90);//输出旋转90度，保持坚屏录制
            switch (mOutFormat) {
                case 0:
                    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 视频输出格式
                    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 音频格式
                    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);// 视频录制格式
                    break;
                case 1:
                    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
                    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
                    break;
            }

            //设置视频录制的分辨率。必须放在设置编码和格式的后面，否则报错
            mMediaRecorder.setVideoSize(mXpx, mYpx);
            // 设置录制的视频帧率。必须放在设置编码和格式的后面，否则报错
            mMediaRecorder.setVideoFrameRate(mVideoFrameRate);
            mMediaRecorder.setVideoEncodingBitRate(mVideoEncodingBitRate);
            // mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);
            mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath());

            mMediaRecorder.prepare();
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            mMediaRecorder.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 开始录制视频
     */
    public void record(final OnRecordStatusChangeListener onRecordStausChangeListener) {
        this.mOnRecordStatusChangeListener = onRecordStausChangeListener;
        createRecordDir();
        try {
            if (mOnRecordStatusChangeListener != null)
                mOnRecordStatusChangeListener.onRecordStart();

            if (!mIsOpenCamera)//如果未打开摄像头，则打开
                openCamera();
            initRecord();
            mTimeCount = 0;//时间计数器重新赋值
            mTimer = new Timer();
            mTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    mTimeCount++;
                    if (mOnRecordStatusChangeListener != null)
                        mOnRecordStatusChangeListener.onRecording(mTimeCount, mRecordMaxTime);
                    if (mTimeCount == mRecordMaxTime) {//达到指定时间，停止拍摄
                        stop();
                        if (mOnRecordStatusChangeListener != null)
                            mOnRecordStatusChangeListener.onRecordFinish();
                    }
                }
            }, 0, 1000);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 停止录制并释放相机资源
     */
    public void stop() {
        stopRecord();
        releaseRecord();
        freeCameraResource();
    }

    /**
     * 停止录制
     */
    public void stopRecord() {
        if (mTimer != null)
            mTimer.cancel();
        if (mMediaRecorder != null) {
            //设置后不会崩
            mMediaRecorder.setOnErrorListener(null);
            mMediaRecorder.setPreviewDisplay(null);
            try {
                mMediaRecorder.stop();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 释放资源
     */
    private void releaseRecord() {
        if (mMediaRecorder != null) {
            mMediaRecorder.setOnErrorListener(null);
            try {
                mMediaRecorder.release();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        mMediaRecorder = null;
    }

    /**
     * 录制状态变化回调接口
     */
    public interface OnRecordStatusChangeListener {

        void onRecordFinish();

        void onRecording(int timeCount, int recordMaxTime);

        void onRecordStart();
    }

    @Override
    public void onError(MediaRecorder mr, int what, int extra) {
        try {
            if (mr != null)
                mr.reset();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @return 已经录制的秒数
     */
    public int getTimeCount() {
        return mTimeCount;
    }

    /**
     * @return 录制的视频文件
     */
    public File getRecordFile() {
        return mVideoFile;
    }


}
